package cn.lg.soar.core.handler.ex;

import cn.lg.soar.common.data.msg.I18n;
import cn.lg.soar.common.exceptions.*;
import cn.lg.soar.common.model.HttpResult;
import cn.lg.soar.common.util.Validator;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.ConnectException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 全局拦截异常处理异常
 * @author luguoxiang
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
public class SoarExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(SoarExceptionHandler.class);

    private static List<ExHandler<?>> list = new ArrayList<>();

    static {
        list.add(new ExHandler<>(
                BaseException.class,
                e -> new ErrorResult(500, HttpResult.fail((BaseException) e))
        ));
        list.add(new ExHandler<>(
                ParameterException.class,
                e -> new ErrorResult(400, HttpResult.fail((BaseException) e))
        ));
        list.add(new ExHandler<>(
                ConstraintViolationException.class,
                e -> {
                    // 获取校验信息
                    Set<ConstraintViolation<?>> violations = ((ConstraintViolationException) e).getConstraintViolations();
                    // 拼接提示信息
                    String error = violations.stream()
                            .map(ConstraintViolation::getMessage)
                            .collect(Collectors.joining());
                    // 创建响应数据
                    BaseAException aException = new BaseAException(100, String.join(",", error));
                    HttpResult<Map<String, String>> fail = HttpResult.fail(aException);
                    // 转换明细
                    fail.setData(
                            violations.stream().collect(
                                    Collectors.toMap(x -> x.getPropertyPath().toString(), ConstraintViolation::getMessage)
                            )
                    );
                    return new ErrorResult(400, fail);
                }
        ));
        list.add(new ExHandler<>(
                AuthenticationException.class,
                e -> new ErrorResult(401, HttpResult.fail((AuthenticationException) e))
        ));
        list.add(new ExHandler<>(
                PermissionException.class,
                e -> new ErrorResult(403, HttpResult.fail((PermissionException) e))
        ));
        list.add(new ExHandler<>(
                ConnectException.class,
                e -> new ErrorResult(502, HttpResult.fail(new BaseBException(2, I18n.build("服务未发现"))))
        ));
        sorted();
    }

    /**
     * 排序
     */
    public static void sorted() {
        list = list.stream()
                .distinct()
                .sorted((a, b) -> {
                    if (a.type.isAssignableFrom(b.type)) {
                        return 1;
                    }
                    if (b.type.isAssignableFrom(a.type)) {
                        return -1;
                    }
                    return 0;
                })
                .collect(Collectors.toList());
    }

    /**
     * 注册异常类型处理方式
     * @param type 异常类型
     * @param handler 处理方式
     */
    public static void register(Class<? extends Throwable> type, Function<Throwable, ErrorResult> handler) {
        list.add(new ExHandler<>(type, handler));
        sorted();
    }

    /**
     * 处理异常
     * @param exception 异常
     * @param envInfo 环境信息
     * @return
     */
    public static ErrorResult handle(Throwable exception, String envInfo) {
        Class<? extends Throwable> aClass = exception.getClass();
        for (ExHandler<?> exHandler : list) {
            if (exHandler.support(aClass)) {
                ErrorResult result = exHandler.handle(exception);
                LOGGER.warn("{}", envInfo, exception);
                return result;
            }
        }
        // 兜底
        LOGGER.error("未知异常\nenvInfo：{}", envInfo, exception);
        return guaranteed(exception);
    }

    /**
     * 处理异常
     * @param exception 异常
     * @param consumer 处理日志
     * @return
     */
    public static ErrorResult handle(Throwable exception, Consumer<String> consumer) {
        Class<? extends Throwable> aClass = exception.getClass();
        for (ExHandler<?> exHandler : list) {
            if (exHandler.support(aClass)) {
                ErrorResult result = exHandler.handle(exception);
                consumer.accept(result.getResult().getMsg());
                return result;
            }
        }
        // 兜底
        LOGGER.error("未知异常", exception);
        return guaranteed(exception);
    }

    /**
     * 处理异常
     * @param exception 异常
     * @return
     */
    public static ErrorResult handle(Throwable exception) {
        Class<? extends Throwable> aClass = exception.getClass();
        for (ExHandler<?> exHandler : list) {
            if (exHandler.support(aClass)) {
                ErrorResult result = exHandler.handle(exception);
                LOGGER.warn("{}", result.getResult().getMsg(), exception);
                return result;
            }
        }
        // 兜底
        LOGGER.error("未知异常", exception);
        return guaranteed(exception);
    }

    /**
     * 兜底
     * @param e
     * @return
     */
    private static ErrorResult guaranteed(Throwable e) {
        String message = e.getMessage();
        if (message == null || message.length() > 20 || !Validator.isChinese(message)) {
            message = "服务器繁忙！请稍后重试！";
        }
        return new ErrorResult(500, HttpResult.fail("B0000", I18n.build(message)));
    }

}
